package com.ego.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ego.exception.EgoException;
import com.ego.exception.ExceptionEnum;
import com.ego.mapper.*;
import com.ego.pojo.*;
import com.ego.pojo.vo.ResultVO;
import com.ego.pojo.bo.SpuBo;
import com.ego.service.SpuService;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;

/**
 * @author Ares
 * @date 2020-11-25
 */
@Service
@Slf4j
public class SpuServiceImpl implements SpuService {

    @Resource
    private SpuMapper spuMapper;

    @Resource
    private SpuDetailMapper spuDetailMapper;

    @Resource
    private SkuMapper skuMapper;

    @Resource
    private StockMapper stockMapper;

    @Resource
    private AmqpTemplate amqpTemplate;

    @Override
    @Transactional(readOnly = true)
    public ResultVO<SpuBo> getSpuBo(String key, Boolean saleable, Integer page, Integer rows) {
        Integer beginRow = (page-1)*rows;
        Integer count = spuMapper.selectCount(new QueryWrapper<Spu>()
                .eq(saleable!=null,"saleable",saleable)
                .like(StringUtils.isNotEmpty(key),"title",key));
        List<SpuBo> list = spuMapper.selectSpuBo(key,saleable,beginRow,rows);
        ResultVO<SpuBo> vo = new ResultVO<>();
        vo.setItems(list);
        vo.setTotal(Long.valueOf(count));
        return vo;
    }

    @Override
    @Transactional(readOnly = true)
    public ResultVO<SpuBo> page(Boolean saleable, Integer page, Integer rows) {
        Integer beginRow = (page-1)*rows;
        Integer count = spuMapper.selectCount(new QueryWrapper<Spu>()
                .eq(saleable!=null,"saleable",saleable));
        List<SpuBo> list = spuMapper.selectPage(saleable, page, rows);
        ResultVO<SpuBo> vo = new ResultVO<>();
        vo.setItems(list);
        vo.setTotal(Long.valueOf(count));
        return vo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteSpu(Long id) {
        Spu spu = new Spu();
        spu.setId(id);
        spu.setValid(false);
        sendMessage(id,"delete");
        spuMapper.updateById(spu);
    }

    @Override
    @Transactional(readOnly = true)
    public SpuDetail getSpuDetailById(Long sid) {
        return spuDetailMapper.selectById(sid);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveGoods(SpuBo spuBo) {
        spuBo.setCreateTime(new Date());
        spuBo.setLastUpdateTime(spuBo.getCreateTime());
        spuMapper.insert(spuBo);
        spuBo.getSpuDetail().setSpuId(spuBo.getId());
        spuDetailMapper.insert(spuBo.getSpuDetail());
        List<Sku> skus = spuBo.getSkus();
        for (Sku sku : skus) {
            sku.setSpuId(spuBo.getId());
            sku.setCreateTime(spuBo.getCreateTime());
            sku.setLastUpdateTime(spuBo.getCreateTime());
            skuMapper.insert(sku);
            sku.getStock().setSkuId(sku.getId());
            stockMapper.insert(sku.getStock());
        }
        sendMessage(spuBo.getId(),"insert");
    }

    @Override
    @Transactional(readOnly = true)
    public List<Sku> getSkus(Long spuId) {
        return skuMapper.selectListBySpuId(spuId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateGoods(SpuBo spuBo) {
        spuBo.setCreateTime(new Date());
        spuBo.setLastUpdateTime(spuBo.getCreateTime());
        spuMapper.updateById(spuBo);
        spuBo.getSpuDetail().setSpuId(spuBo.getId());
        spuDetailMapper.updateById(spuBo.getSpuDetail());
        List<Sku> skus = spuBo.getSkus();
        skuMapper.delete(new QueryWrapper<Sku>().eq("spu_id",spuBo.getId()));
        for (Sku sku : skus) {
            stockMapper.deleteById(sku.getId());
            sku.setSpuId(spuBo.getId());
            sku.setCreateTime(spuBo.getCreateTime());
            sku.setLastUpdateTime(spuBo.getCreateTime());
            skuMapper.insert(sku);
            sku.getStock().setSkuId(sku.getId());
            stockMapper.insert(sku.getStock());
        }
        sendMessage(spuBo.getId(),"update");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void editSaleable(SpuBo spuBo) {
        spuMapper.updateById(spuBo);
    }

    @Override
    public SpuBo queryGoodsById(Long id) {
        Spu spu = spuMapper.selectById(id);
        SpuDetail spuDetail = spuDetailMapper.selectById(spu.getId());
        QueryWrapper<Sku> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("spu_id",spu.getId());
        List<Sku> skus = skuMapper.selectList(queryWrapper);
        List<Long> skuIdList = new ArrayList<>();
        for (Sku sku : skus) {
            skuIdList.add(sku.getId());
        }
        List<Stock> stocks = stockMapper.selectBatchIds(skuIdList);
        for (Sku sku : skus) {
            for (Stock stock : stocks) {
                if(sku.getId().equals(stock.getSkuId())){
                    sku.setStock(stock);
                }
            }
        }
        return SpuBo.builder()
                .id(spu.getId())
                .brandId(spu.getBrandId())
                .cid1(spu.getCid1())
                .cid2(spu.getCid2())
                .cid3(spu.getCid3())
                .brandId(spu.getBrandId())
                .title(spu.getTitle())
                .subTitle(spu.getSubTitle())
                .saleable(spu.getSaleable())
                .valid(spu.getValid())
                .createTime(spu.getCreateTime())
                .lastUpdateTime(spu.getLastUpdateTime())
                .skus(skus)
                .spuDetail(spuDetail).build();
    }

    @Override
    @Transactional(readOnly = true)
    public Sku querySkuById(Long skuId) {
        return skuMapper.selectById(skuId);
    }

    @Override
    @Transactional(readOnly = true)
    public List<Sku> querySkuListByIds(List<Long> skuIdList) {
        return skuMapper.selectBatchIds(skuIdList);
    }

    @GlobalTransactional(name = "submitOrder")
    @Override
    public void descrementStock(Map<Long, Integer> stockData) {
        try {
            stockData.forEach((skuId, stock) -> {
                int count = stockMapper.descrementStock(skuId, stock);
                if (count == 0) {
                    EgoException.error(log,ExceptionEnum.STOCK_NOT_ENOUGH);
                }
            });
        } catch (Exception e) {
            EgoException.error(log,ExceptionEnum.STOCK_NOT_ENOUGH,e);
        }
    }

    @Override
    @Transactional(readOnly = true)
    public List<Stock> findStockListBySkuIdList(List<Long> skuIdList) {
        return stockMapper.selectBatchIds(skuIdList);
    }

    private void sendMessage(Long id,String type){
        try{
            amqpTemplate.convertAndSend("item."+type,id);
        }catch (Exception e){
            log.error("{}商品消息发送异常，商品id：{}", type, id, e);
            EgoException.error(log, ExceptionEnum.SEND_MSG_EXCEPTION,e);
        }
    }

    @GlobalTransactional(name = "submitSeckillOrder")
    public void descrementSeckillStock(Long skuId) {
        int count = stockMapper.descrementSeckillStock(skuId);
        if (count == 0) {
            EgoException.error(log,ExceptionEnum.STOCK_NOT_ENOUGH);
        }
    }
}
